package iot.utils;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * 通信协议数据解析工具类
 */
public class DataPackUtil {
    /**
     * 默认字符集GBK常量字符串
     */
    public static final String DEFAULT_CHARSET_GBK = "GBK";

    /**
     * 默认字符集UTF-8常量字符串
     */
    public static final String DEFAULT_CHARSET_UTF8 = "UTF-8";

    public static int readWord(byte[] bytes, int start) {
        return (bytes[start] & 0xFF) << 8 | (bytes[start + 1] & 0XFF);
    }

    public static long readDWord(byte[] bytes, int start) {
        return (bytes[start] & 0xFFL) << 24
                | (bytes[start+ 1] & 0xFF) << 16
                | (bytes[start+ 2] & 0xFF) << 8
                | (bytes[start+ 3] & 0xFF);
    }

    public static String readBCD(byte[] bs, int start, int size) {
        StringBuilder sb = new StringBuilder();
        for (int i = start; i < start + size; i++) {
            sb.append(bs[i] >> 4 & 0x0F);
            sb.append(bs[i] & 0x0F);
        }
        return sb.toString();
    }

    public static byte[] getLongBytes(long val, int length) {
        if (1 > length || 8 < length) {
            throw new IllegalArgumentException("the length is bigger than 0 or less than 8");
        }
        byte[] returnBytes = new byte[length];
        for (int i = 0; i < length; i++) {
            returnBytes[length - i - 1] = (byte) ((val >> (8 * i)) & 0xFF);
        }
        return returnBytes;
    }

    /**
     * 获得整型数值的字节码信息<br>
     * 高位在前，低位在后。网络字节顺序，大尾端。
     *
     * @param integer 数值
     * @param length  字节数组长度，取值1,2,4
     * @return
     */
    public static byte[] getIntegerBytes(int integer, int length) {
        if (1 > length || 4 < length) {
            throw new IllegalArgumentException("the length is bigger than 0 or less than 4");
        }
        byte[] returnBytes = new byte[length];
        for (int i = 0; i < length; i++) {
            returnBytes[length - i - 1] = (byte) ((integer >> (8 * i)) & 0xFF);
        }
        return returnBytes;
    }

    /**
     * 获得WORD的字节码列表信息(2个字节)
     *
     * @param integer 数值
     * @return
     */
    public static List<Byte> getWordBytes(int integer) {
        byte[] bytes = getIntegerBytes(integer, 2);
        List<Byte> byteList = new ArrayList<>();
        for (int i = 0; i < bytes.length; i++) {
            byteList.add(bytes[i]);
        }
        return byteList;
    }

    /**
     * 获得DWORD的字节码列表信息(4个字节)
     *
     * @param integer 数值
     * @return
     */
    public static List<Byte> getDWordBytes(int integer) {
        byte[] bytes = getIntegerBytes(integer, 4);
        List<Byte> byteList = new ArrayList<>();
        for (int i = 0; i < bytes.length; i++) {
            byteList.add(bytes[i]);
        }
        return byteList;
    }

    /**
     * 获得复合BCD码字符串的字节码信息<br>
     * 复合BCD码字符：一个字节表示2个数字
     *
     * @param number 复合BCD码字符串，字符串长度必须是偶数
     * @return
     */
    public static List<Byte> getBCDBytes(String number) {
        if (null == number) {
            throw new IllegalArgumentException("number is null");
        }
        if (0 != number.length() % 2) {
            throw new IllegalArgumentException("the length of number is odd");
        }
        if (Pattern.matches("\\\\b*", number)) {
            throw new IllegalArgumentException("number is not bcd string");
        }
        List<Byte> returnBytes = new ArrayList<>();
        for (int i = 0; i < number.length(); i = i + 2) {
            returnBytes.add(Integer.valueOf(number.charAt(i) + "" + number.charAt(i + 1), 16).byteValue());
        }
        return returnBytes;
    }

    public static byte[] byteListToArray(List<Byte> list) {
        byte[] result = new byte[list.size()];
        for (int i = 0; i < result.length; i++) {
            result[i] = list.get(i);
        }
        return result;
    }

    public static List<Byte> byteArrayToList(byte[] bytes) {
        List<Byte> list = new ArrayList<>(bytes.length);
        for (byte b : bytes) {
            list.add(b);
        }
        return list;
    }

    /**
     * 字节数组转换成字符串
     * @param bytes
     * @param start 起点索引，包含
     * @param end 终点索引，不包含
     * @return
     */
    public static String bytesToString(byte[] bytes, int start, int end) {
        List<Byte> byteList = new ArrayList<>();
        for (int i = start; i < end; i++) {
            byte b = bytes[i];
            if(0x00 != b) {
                byteList.add(b);
            }
        }
        try {
            return new String(byteListToArray(byteList), DEFAULT_CHARSET_GBK);
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    public static byte[] stringToByteArray(String str) {
        try {
            return str.getBytes(DataPackUtil.DEFAULT_CHARSET_GBK);
        } catch (UnsupportedEncodingException e) {
            return new byte[0];
        }
    }

    public static void writeByte(byte[] dest, int index, int b) {
        System.arraycopy(getIntegerBytes(b, 1), 0, dest, index, 1);
    }

    public static void writeBytes(byte[] dest, int index, byte[] src) {
        System.arraycopy(src, 0, dest, index, src.length);
    }

    public static void writeWord(byte[] dest, int index, int word) {
        System.arraycopy(getIntegerBytes(word, 2), 0, dest, index, 2);
    }

    public static void writeDWord(byte[] dest, int index, int dword) {
        System.arraycopy(getIntegerBytes(dword, 4), 0, dest, index, 4);
    }

    public static void writeString(byte[] dest, int index, String str) {
        byte[] sb = str.getBytes();
        System.arraycopy(sb, 0, dest, index, sb.length);
    }

    /**
     * 计算checksum，不包含end字节
     * @param bs
     * @param start
     * @param end
     * @return
     */
    public static byte checksum(byte[] bs, int start, int end) {
        byte check = bs[start];
        for (int i = start + 1; i < end; i++) {
            check ^= bs[i];
        }
        return check;
    }

    /**
     * 计算checksum，不包含end字节
     * @param bytes
     * @param start
     * @param end
     * @return
     */
    public static byte checksum(List<Byte> bytes, int start, int end) {
        byte check = bytes.get(start);
        for (int i = start + 1; i < end; i++) {
            check ^= bytes.get(i);
        }
        return check;
    }

    public static byte[] getBytesFromUUID(UUID uuid) {
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(uuid.getMostSignificantBits());
        bb.putLong(uuid.getLeastSignificantBits());

        return bb.array();
    }

    public static UUID getUUIDFromBytes(byte[] bytes) {
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        Long high = byteBuffer.getLong();
        Long low = byteBuffer.getLong();

        return new UUID(high, low);
    }

    public static UUID uuidStartOf(long timestamp) {
        return new UUID(timestamp, 0);
    }

    public static UUID uuidEndOf(long timestamp) {
        return new UUID(timestamp, Long.MAX_VALUE);
    }

}
